#include <camera.hpp>
#include <lightsourceRenderer.hpp>

#define LIGHTTYPE_DIRECTIONAL 0

struct Light {
    glm::vec3 position;
    glm::vec3 direction;

    glm::vec3 clrAmbient;
    glm::vec3 clrDiffuse;
    glm::vec3 clrSpecular;

    float constant;
    float linear;
    float quadratic;
    float innerCutoff;
    float outterCutoff;
};

struct ObjectInfo {
    int diffuseMap;
    int specularMap;
    float shininess;
};

class RendererBase {
protected:
    Shader shaderObj;
public:
    RendererBase(std::string vsPath, std::string fsPath);
    bool ready();
    void use();
    void virtual setLight(Light &light) = 0;
    void virtual setCameraInfo(CamInfo &camInfo) = 0;
    void virtual setObjectInfo(ObjectInfo &objInfo) = 0;
    void virtual setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat) = 0;
};
RendererBase::RendererBase(std::string vsPath, std::string fsPath) : shaderObj(vsPath.c_str(), fsPath.c_str()){}
bool RendererBase::ready()
{
    return shaderObj.ready();
}
void RendererBase::use()
{
    shaderObj.use();
}

class L11Directional : public RendererBase {
public:
    L11Directional(std::string bp);
    void setLight(Light &light);
    void setCameraInfo(CamInfo &camInfo);
    void setObjectInfo(ObjectInfo &objInfo);
    void setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat);
};
L11Directional::L11Directional(std::string bp) : RendererBase( bp + "\\lesson11_vs.glsl", bp + "\\lesson11_fs_directional.glsl" ){}
void L11Directional::setLight(Light &light)
{
    shaderObj.setVec3("light.direction", light.direction);
    shaderObj.setVec3("light.ambient", light.clrAmbient);
    shaderObj.setVec3("light.diffuse", light.clrDiffuse);
    shaderObj.setVec3("light.specular", light.clrSpecular);
}
void L11Directional::setCameraInfo(CamInfo &camInfo)
{
    shaderObj.setVec3("viewPos", camInfo.camPos);
    shaderObj.setMat4("viewMat", camInfo.view);
    shaderObj.setMat4("projMat", camInfo.projection);
}
void L11Directional::setObjectInfo(ObjectInfo &objInfo)
{
    shaderObj.setInt("material.diffuse", objInfo.diffuseMap);
    shaderObj.setInt("material.specular", objInfo.specularMap);
    shaderObj.setFloat("material.shininess", objInfo.shininess);
}
void L11Directional::setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat)
{
    shaderObj.setMat4("modelMat", modleMat);
    shaderObj.setMat3("normalMat", normalMat);
}

class L11Point : public RendererBase {
public:
    L11Point(std::string bp);
    void setLight(Light &light);
    void setCameraInfo(CamInfo &camInfo);
    void setObjectInfo(ObjectInfo &objInfo);
    void setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat);
};
L11Point::L11Point(std::string bp) : RendererBase( bp + "\\lesson11_vs.glsl", bp + "\\lesson11_fs_point.glsl" ){}
void L11Point::setLight(Light &light)
{
    shaderObj.setVec3("light.position", light.position);
    shaderObj.setVec3("light.ambient", light.clrAmbient);
    shaderObj.setVec3("light.diffuse", light.clrDiffuse);
    shaderObj.setVec3("light.specular", light.clrSpecular);

    shaderObj.setFloat("light.constant", light.constant);
    shaderObj.setFloat("light.linear", light.linear);
    shaderObj.setFloat("light.quadratic", light.quadratic);
}
void L11Point::setCameraInfo(CamInfo &camInfo)
{
    shaderObj.setVec3("viewPos", camInfo.camPos);
    shaderObj.setMat4("viewMat", camInfo.view);
    shaderObj.setMat4("projMat", camInfo.projection);
}
void L11Point::setObjectInfo(ObjectInfo &objInfo)
{
    shaderObj.setInt("material.diffuse", objInfo.diffuseMap);
    shaderObj.setInt("material.specular", objInfo.specularMap);
    shaderObj.setFloat("material.shininess", objInfo.shininess);
}
void L11Point::setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat)
{
    shaderObj.setMat4("modelMat", modleMat);
    shaderObj.setMat3("normalMat", normalMat);
}

class L11Spot : public RendererBase {
public:
    L11Spot(std::string bp);
    void setLight(Light &light);
    void setCameraInfo(CamInfo &camInfo);
    void setObjectInfo(ObjectInfo &objInfo);
    void setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat);
};
L11Spot::L11Spot(std::string bp) : RendererBase( bp + "\\lesson11_vs.glsl", bp + "\\lesson11_fs_spot.glsl" ){}
void L11Spot::setLight(Light &light)
{
    shaderObj.setVec3("light.position", light.position);
    shaderObj.setVec3("light.direction", light.direction);
    shaderObj.setVec3("light.ambient", light.clrAmbient);
    shaderObj.setVec3("light.diffuse", light.clrDiffuse);
    shaderObj.setVec3("light.specular", light.clrSpecular);

    shaderObj.setFloat("light.innerCutoff", light.innerCutoff);
    shaderObj.setFloat("light.outterCutoff", light.outterCutoff);
}
void L11Spot::setCameraInfo(CamInfo &camInfo)
{
    shaderObj.setVec3("viewPos", camInfo.camPos);
    shaderObj.setMat4("viewMat", camInfo.view);
    shaderObj.setMat4("projMat", camInfo.projection);
}
void L11Spot::setObjectInfo(ObjectInfo &objInfo)
{
    shaderObj.setInt("material.diffuse", objInfo.diffuseMap);
    shaderObj.setInt("material.specular", objInfo.specularMap);
    shaderObj.setFloat("material.shininess", objInfo.shininess);
}
void L11Spot::setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat)
{
    shaderObj.setMat4("modelMat", modleMat);
    shaderObj.setMat3("normalMat", normalMat);
}

